package com.sugo.seckill.order.service.impl;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import com.github.pagehelper.Page;
import com.sugo.seckill.aop.ServiceLock;
import com.sugo.seckill.aop.ServiceRedisLock;
import com.sugo.seckill.distributedlock.rocketmq.RocketMQSender;
import com.sugo.seckill.distributedlock.zk.ZkLockUtils;
import com.sugo.seckill.http.HttpResult;
import com.sugo.seckill.http.HttpStatus;
import com.sugo.seckill.mapper.order.SeckillGoodsMapper;
import com.sugo.seckill.mapper.order.SeckillOrderMapper;
import com.sugo.seckill.mapper.pay.TbPayLogMapper;
import com.sugo.seckill.order.service.SeckillOrderService;
import com.sugo.seckill.page.PageResult;
import com.sugo.seckill.pojo.*;
import com.sugo.seckill.queue.jvm.SeckillQueue;
import com.sugo.seckill.utils.IdWorker;
import com.sugo.seckill.utils.ObjectUtils;
import com.sugo.seckill.distributedlock.redis.RedisLockUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;

import com.github.pagehelper.PageHelper;

import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.Resource;

/**
 * 服务实现层
 * @author Administrator
 *
 */
@Service
public class SeckillOrderServiceImpl implements SeckillOrderService {

	@Resource
	private SeckillOrderMapper seckillOrderMapper;

	/** 注入支付 */
	@Resource
	private TbPayLogMapper payLogMapper;


	/** 注入商品对象 */
	@Resource
	private SeckillGoodsMapper seckillGoodsMapper;

	/** 事务管理 */
	@Autowired
	DataSourceTransactionManager dataSourceTransactionManager;
	@Autowired
	TransactionDefinition transactionDefinition;

	/** 注入redis */
	@Autowired
	private RedisTemplate redisTemplate;

	/** 注入Idworker */
	@Autowired
	private IdWorker idWorker;

	/** 成功的编码 */
	private final static int SUCCESS = 200;

	/** rocketMq的生产者(发送者) */
	/// @Autowired
	private RocketMQSender mqSender;

	/**
	 * 程序锁
	 * 互斥锁 参数默认false，不公平锁
	 */
	private final Lock lock = new ReentrantLock(true);


	/** 日志 */
	private final static Logger LOGGER = LoggerFactory.getLogger(SeckillOrderServiceImpl.class);

	/** 获取当前可用的cpu核心数 */
	private static int corePoolSize = Runtime.getRuntime().availableProcessors();

	/** 创建线程池  调整队列数 拒绝服务 */
	private static ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,
			corePoolSize + 1,
			10L,
			TimeUnit.SECONDS,
			new LinkedBlockingQueue<>(1000),
			r -> new Thread(r, "SeckillOrderService-Thread"));


	@Override
	public void getSeckillGoods(Long seckillId){

		redisTemplate.delete("seckill_goods_"+seckillId);
		redisTemplate.delete("seckill_goods_stock_"+seckillId);

		//创建example对象
		Example example = new Example(TbSeckillGoods.class);
		Example.Criteria criteria = example.createCriteria();

		//设置查询条件
		//状态可用
		criteria.andEqualTo("status",1);
		//时间必须在活动区间
		criteria.andCondition("now() BETWEEN start_time_date AND end_time_date");
		//库存必须大于0
		criteria.andGreaterThan("stockCount",0);

		//获取Redis中所有的key,实现排除当前Redis中已经存在的商品
		Set<Long> ids = redisTemplate.boundHashOps("seckillGoods").keys();
		//判断
		if(ids!=null && ids.size()>0){
			criteria.andNotIn("id",ids);
		}

		//查询
		List<TbSeckillGoods> seckillGoodsList = seckillGoodsMapper.selectByExample(example);

		//判断是否有入库商品
		if(seckillGoodsList!=null && seckillGoodsList.size()>0){
			//循环
			for (TbSeckillGoods goods : seckillGoodsList) {
				//放入商品对象
				redisTemplate.opsForValue().set("seckill_goods_"+goods.getId(),goods);
				//存储库存
				redisTemplate.opsForValue().set("seckill_goods_stock_"+goods.getId(),goods.getStockCount());
			}
		}
	}


	/**
	 * 按分页查询
	 */
	@Override
	@Transactional
	public PageResult findPage(int pageNum, int pageSize) {
		PageHelper.startPage(pageNum, pageSize);
		Page<TbSeckillOrder> page=   (Page<TbSeckillOrder>) seckillOrderMapper.selectByExample(null);
		return new PageResult(page.getTotal(), page.getResult());
	}

	/**
	 * 根据ID获取实体
	 */
	@Override
	@Transactional
	public TbSeckillOrder findOne(Long id){
		return seckillOrderMapper.selectByPrimaryKey(id);
	}

	/**
	 * 该商品是否有效
	 */
	private HttpResult validGoods(TbSeckillGoods seckillGoods){
		if(seckillGoods==null){
			return HttpResult.error(HttpStatus.SC_NOT_FOUND,"商品不存在");
		}
		if(1 != seckillGoods.getStatus() ){
			return HttpResult.error(HttpStatus.SC_NOT_FOUND,"商品未审核");
		}
		if(seckillGoods.getStockCount()<=0){
			return HttpResult.error(HttpStatus.SC_NOT_FOUND,"商品已抢购一空");
		}
		long time = System.currentTimeMillis();
		if(seckillGoods.getStartTimeDate().getTime()> time){
			return HttpResult.error(HttpStatus.SC_NOT_FOUND,"活动未开始");
		}
		if(seckillGoods.getEndTimeDate().getTime()<= time){
			return HttpResult.error(HttpStatus.SC_NOT_FOUND,"活动已结束");
		}
		return HttpResult.ok();
	}

	/**
	 * 扣减库存
	 * @param seckillId 商品ID
	 * @param orderId 订单ID
	 * @param seckillGoods 商品信息
	 */
	private void DeductionInventory(long seckillId, long orderId, TbSeckillGoods seckillGoods){
		LOGGER.info("开始扣减库存。商品ID {},订单Id{}",seckillId, orderId);
		//扣减库存
		seckillGoods.setStockCount(seckillGoods.getStockCount()-1);
		//销量加1
		seckillGoods.setSales(seckillGoods.getSales()+1);
		//更新秒杀信息
		seckillGoodsMapper.updateByPrimaryKey(seckillGoods);
	}

	/**
	 * 生成订单，订单状态为：0
	 * @param orderId 订单号
	 * @param seckillGoods 商品信息
	 * @param userId 用户ID
	 */
	private TbSeckillOrder newSkillOrder(long orderId, TbSeckillGoods seckillGoods,String userId){
		TbSeckillOrder skillOrder=new TbSeckillOrder();
		skillOrder.setId(orderId);
		skillOrder.setCreateTime(new Date());
		skillOrder.setUserId(userId);
		//秒杀价格
		skillOrder.setMoney(seckillGoods.getPrice());
		skillOrder.setSeckillId(ObjectUtils.toLong(seckillGoods.getId()));
		//状态
		skillOrder.setStatus("0");
		return skillOrder;
	}
	
	/**
	 * 方式1-1：普通的下单方式,会出现库存超卖的问题
	 * @author hubin
	 * @date 2020/6/10 10:54
	 */
	@Transactional
	@Override
	public HttpResult startSubmitOrder(Long seckillId, String userId) {
		long orderId = idWorker.nextId();
		try {
			LOGGER.info("开始下单条件校验。商品ID {},订单Id{}", seckillId, orderId);
			//从数据库中查询商品信息
			TbSeckillGoods seckillGoods = seckillGoodsMapper.selectByPrimaryKey(seckillId);

			HttpResult result = validGoods(seckillGoods);
			if (result.getCode() != SUCCESS) {
				return result;
			}
			//扣减库存
			DeductionInventory(seckillId, orderId, seckillGoods);

			LOGGER.info("开始下单,订单号：{}", orderId);
			TbSeckillOrder skillOrder=newSkillOrder(orderId,seckillGoods,userId);

			//保存订单
			seckillOrderMapper.insertSelective(skillOrder);


		} catch (RuntimeException e) {
			e.printStackTrace();
		}
		return HttpResult.ok(orderId+"");
	}


	/**
	 * 方式1-2：普通的下单方式(多线程),会出现库存超卖的问题
	 * <pre>
	 *     1.从数据库读取商品改为从redis缓存读取
	 *     2.添加异步添加订单
	 * </pre>
	 * @author hubin
	 * @date 2020/6/10 10:54
	 */
	@Transactional
	@Override
	public HttpResult startSubmitOrderMultiThread(Long seckillId, String userId) {
		long orderId = idWorker.nextId();
		try {
			//“数据库中查询商品信息”改为“从缓存中获取”
			TbSeckillGoods seckillGoods = (TbSeckillGoods) redisTemplate.opsForValue().get("seckill_goods_" + seckillId);
			HttpResult result = validGoods(seckillGoods);
			if (result.getCode() != SUCCESS) {
				return result;
			}

			//扣减库存
			DeductionInventory(seckillId, orderId, seckillGoods);
			// 将seckillGoos减库存进行录入到 redis
			redisTemplate.opsForValue().set("seckill_goods_" + seckillId, seckillGoods);
			Runnable task = () -> {
				LOGGER.info("异步下单,订单号：{}", orderId);
				TbSeckillOrder skillOrder = newSkillOrder(orderId, seckillGoods, userId);
				//保存订单
				seckillOrderMapper.insertSelective(skillOrder);
			};
			//执行
			executor.execute(task);

		} catch (RuntimeException e) {
			e.printStackTrace();
		}
		return HttpResult.ok(orderId+"");
	}

	/**
	 * <p>方式2-1:使用程序锁实现库存超卖控制</p>
	 * <pre>
	 *     需要自己手动添加事务
	 * </pre>
	 * @author linhx
	 */
	@Override
	public HttpResult startSubmitOrderByReentrantLock(Long seckillId, String userId) {
		long orderId = idWorker.nextId();
		TransactionStatus transactionStatus = null;
		//业务开始生活，加锁
		lock.lock();
		try {
			// 手动开启事务
			transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
			LOGGER.info("开始下单条件校验。商品ID={},订单ID {}", seckillId, orderId);
			// 1、 从数据库中查询商品信息 100线程
			TbSeckillGoods seckillGoods = seckillGoodsMapper.selectByPrimaryKey(seckillId);

			HttpResult result = validGoods(seckillGoods);
			if (result.getCode() != SUCCESS) {
				return result;
			}

			//扣减库存
			DeductionInventory(seckillId, orderId, seckillGoods);

			LOGGER.info("开始下单,订单号：{}", orderId);
			TbSeckillOrder skillOrder = newSkillOrder(orderId, seckillGoods, userId);

			//保存订单
			seckillOrderMapper.insertSelective(skillOrder);
			// 提交事务
			dataSourceTransactionManager.commit(transactionStatus);
		} catch (RuntimeException e) {
			//事务回滚
			if (transactionStatus != null) {
				dataSourceTransactionManager.rollback(transactionStatus);
			}
			e.printStackTrace();
		}finally {
			//释放锁
			lock.unlock();
		}
		return HttpResult.ok(orderId+"");
	}

	/**
	 * <p>3-1 使用aop锁实现库存超卖控制</p>
	 * <pre>
	 *     添加@ServiceLock
	 * </pre>
	 * @author hubin
	 * @date 2020/9/5 20:48
	 */
	@Transactional
	@ServiceLock
	@Override
	public HttpResult startSubmitOrderByAOPLock(Long seckillId, String userId) {
		long orderId = idWorker.nextId();
		try {
			LOGGER.info("开始下单条件校验。商品ID={},订单ID {}", seckillId, orderId);
			//商品数据(从缓存中获取)
			TbSeckillGoods seckillGoods = (TbSeckillGoods) redisTemplate.opsForValue().get("seckill_goods_" + seckillId);
			HttpResult result = validGoods(seckillGoods);
			if (result.getCode() != SUCCESS) {
				return result;
			}

			//扣减库存
			DeductionInventory(seckillId, orderId, seckillGoods);
			// 将seckillGoos减库存进行录入到 redis
			redisTemplate.opsForValue().set("seckill_goods_" + seckillId, seckillGoods);
			LOGGER.info("开始下单,订单号：{}", orderId);
			TbSeckillOrder skillOrder = newSkillOrder(orderId, seckillGoods, userId);

			//保存订单
			seckillOrderMapper.insertSelective(skillOrder);

		} catch (RuntimeException e) {
			e.printStackTrace();
		}
		return HttpResult.ok(orderId+"");
	}


	/**
	 * 4-1 使用队列的方式实现库存控制
	 * @author hubin
	 * @date 2020/9/5 20:48
	 */
	@Override
	public HttpResult startSubmitOrderByBlockingQueue(Long seckillId, String userId) {

		//生成订单id,保证idWorker是单例的状态，否则id冲突W
		long orderId = idWorker.nextId();
		TbSeckillOrder order = new TbSeckillOrder();
		order.setId(orderId);
		//设置秒杀id
		order.setSeckillId(seckillId);
		//设置用户id
		order.setUserId(userId);

		//获取对象，把订单添加到队列中
		Boolean flag = null;
		try {
			flag = SeckillQueue.getMailQueue().produce(order);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		//通知用户下单成功，还是失败
		if (flag) {
			LOGGER.info("用户队列方式下单：{} {}", order.getUserId(), "秒杀成功");
			return HttpResult.ok(orderId + "");
		} else {
			LOGGER.info("用户队列方式下单：{} {}", order.getUserId(), "秒杀失败");
			return HttpResult.error(HttpStatus.SC_NOT_FOUND, "秒杀失败");
		}
	}

	/**
	 * 4-2 使用队列+多线程的方式实现库存控制
	 */
	@Override
	public HttpResult startSubmitOrderByBlockingQueueMultiThread(Long seckillId, String userId){
		try{
			Runnable task = () -> {
				//生成订单id,保证idWorker是单例的状态，否则id冲突W
				long orderId = idWorker.nextId();
				TbSeckillOrder order = new TbSeckillOrder();
				order.setId(orderId);
				//设置秒杀id
				order.setSeckillId(seckillId);
				//设置用户id
				order.setUserId(userId);

				//获取对象，把订单添加到队列中
				Boolean flag = null;
				try {
					flag = SeckillQueue.getMailQueue().produce(order);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

				//通知用户下单成功，还是失败
				if(flag){
					LOGGER.info("用户队列方式下单：{} {}",order.getUserId(),"秒杀成功");
				}else {
					LOGGER.info("用户队列方式下单：{} {}",order.getUserId(),"秒杀失败");
				}

			};
			executor.execute(task);
		}catch (Exception e){
			e.printStackTrace();
		}
		return HttpResult.ok();
	}

	/**
	 * 5-1 使用数据库悲观锁来进行库存控制
	 * @author hubin
	 * @date 2020/9/5 20:48
	 */
	@Transactional
	@Override
	public HttpResult startSubmitOrderBySQLPessimismLock(Long seckillId, String userId) {
		long orderId = idWorker.nextId();
		try {
			LOGGER.info("开始下单条件校验。商品ID={},订单ID {}", seckillId, orderId);
			// 数据库悲观锁 查询的时候需要排队 "for update"
			TbSeckillGoods seckillGoods = seckillGoodsMapper.selectByPrimaryKeyBySQLLock(seckillId);

			HttpResult result = validGoods(seckillGoods);
			if (result.getCode() != SUCCESS) {
				return result;
			}

			//更新秒杀信息
			seckillGoodsMapper.updateSeckillGoodsByPrimaryKeyByLock(seckillId);

			LOGGER.info("开始下单,订单号：{}", orderId);
			TbSeckillOrder seckillOrder = newSkillOrder(orderId, seckillGoods, userId);

			//保存订单
			seckillOrderMapper.insertSelective(seckillOrder);

		} catch (RuntimeException e) {
			e.printStackTrace();
		}

		return HttpResult.ok(orderId+"");
	}

	/**
	 * 5-2 使用数据库悲观锁来进行库存控制
	 * @author hubin
	 * @date 2020/9/5 20:48
	 */
	@Transactional
	@Override
	public HttpResult startSubmitOrderBySQLPessimismLock2(Long seckillId, String userId) {
		long orderId = idWorker.nextId();
		try {
			LOGGER.info("开始下单条件校验。商品ID={},订单ID {}", seckillId, orderId);
			//商品数据可以从缓存中获取
			TbSeckillGoods seckillGoods = (TbSeckillGoods) redisTemplate.opsForValue().get("seckill_goods_" + seckillId);

			HttpResult result = validGoods(seckillGoods);
			if (result.getCode() != SUCCESS) {
				return result;
			}

			//更新秒杀信息
			seckillGoodsMapper.updateSeckillGoodsByPrimaryKeyByLock(seckillId);

			LOGGER.info("开始下单,订单号：{}", orderId);
			TbSeckillOrder seckillOrder = newSkillOrder(orderId, seckillGoods, userId);

			//保存订单
			seckillOrderMapper.insertSelective(seckillOrder);

		} catch (RuntimeException e) {
			e.printStackTrace();
		}

		return HttpResult.ok(orderId+"");
	}


	/**
	 * 5-3 使用数据库乐观锁进行库存控制
	 * @author hubin
	 * @date 2020/9/5 20:48
	 */
	@Transactional
	@Override
	public HttpResult startSubmitOrderBySQLOptimismLock(Long seckillId, String userId) {
		long orderId = idWorker.nextId();
		try {
			LOGGER.info("开始下单条件校验。商品ID={},订单ID {}", seckillId, orderId);
			//商品数据可以从缓存中获取
			TbSeckillGoods seckillGoods = seckillGoodsMapper.selectByPrimaryKey(seckillId);

			HttpResult result = validGoods(seckillGoods);
			if (result.getCode() != SUCCESS) {
				return result;
			}

			//更新秒杀信息
			int i = seckillGoodsMapper.updateSeckillGoodsByPrimaryKeyByVersion(seckillId, seckillGoods.getVersion());

			//判断
			//乐观锁更新成功，后，才能开始下单
			if(i>0){
				LOGGER.info("开始下单,订单号：{}", orderId);
				TbSeckillOrder seckillOrder = newSkillOrder(orderId, seckillGoods, userId);
				//保存订单
				seckillOrderMapper.insertSelective(seckillOrder);
			}else{
				//返回
				return HttpResult.error("人太多了！请稍后重试");

			}

		} catch (RuntimeException e) {
			e.printStackTrace();
		}
		return HttpResult.ok(orderId+"");
	}


	/**
	 * 6-1 redis分布式锁的方式进行上锁
	 * @param seckillId 商品id
	 * @param userId 用户id
	 */
	@Transactional
	@Override
	public HttpResult startSubmitOrderByRedissonLock(Long seckillId, String userId) {
		long orderId = idWorker.nextId();
		// 加锁初始化
		boolean isLock = false;
		try {
			// 开始获取redis锁
			// 获取redis锁，尝试等待3s,上锁以后10s后锁自动解除
			isLock = RedisLockUtil.tryLock("seckill_goods_lock_"+seckillId, TimeUnit.SECONDS,3,10);
			if (isLock) {
				LOGGER.info("开始下单条件校验。商品ID={},订单ID {}", seckillId, orderId);
				TbSeckillGoods seckillGoods = (TbSeckillGoods) redisTemplate.opsForValue().get("seckill_goods_" + seckillId);

				HttpResult result = validGoods(seckillGoods);
				if (result.getCode() != SUCCESS) {
					return result;
				}

				DeductionInventory(seckillId, orderId, seckillGoods);
				// 将seckillGoos减库存进行录入到 redis
				redisTemplate.opsForValue().set("seckill_goods_" + seckillId, seckillGoods);

				LOGGER.info("开始下单,订单号：{}", orderId);
				TbSeckillOrder seckillOrder = newSkillOrder(orderId, seckillGoods, userId);
				//保存订单
				seckillOrderMapper.insertSelective(seckillOrder);
			}else{
				//返回
				return HttpResult.error("人太多了！请稍后重试");
			}
		} catch (RuntimeException e) {
			e.printStackTrace();
		}finally {
			if (isLock) {
				//释放锁
				RedisLockUtil.unlock("seckill_goods_lock_" + seckillId);
			}
		}
		return HttpResult.ok(orderId+"");
	}

	/**
	 * 6-2 redis分布式AOP锁的方式进行上锁
	 * @param seckillId 商品id
	 * @param userId 用户id
	 */
	@Transactional
	@ServiceRedisLock
	@Override
	public HttpResult startSubmitOrderByAOPRedissonLock(Long seckillId, String userId) {
		long orderId = idWorker.nextId();
		LOGGER.info("开始下单条件校验。商品ID={},订单ID {}", seckillId, orderId);
		TbSeckillGoods seckillGoods = (TbSeckillGoods) redisTemplate.opsForValue().get("seckill_goods_" + seckillId);
		HttpResult result = validGoods(seckillGoods);
		if (result.getCode() != SUCCESS) {
			return result;
		}

		DeductionInventory(seckillId, orderId, seckillGoods);
		// 将seckillGoos减库存进行录入到 redis
		redisTemplate.opsForValue().set("seckill_goods_" + seckillId, seckillGoods);

		LOGGER.info("开始下单,订单号：{}", orderId);
		TbSeckillOrder seckillOrder = newSkillOrder(orderId, seckillGoods, userId);
		//保存订单
		seckillOrderMapper.insertSelective(seckillOrder);
		return HttpResult.ok(orderId + "");
	}

/**
 * 6-3 redis的increment(自减)方式进行
 * <pre>
 *     redis的increment是原子性操作
 * </pre>
 * @param seckillId 商品id
 * @param userId 用户id
 */
@Transactional
@Override
public HttpResult startSubmitOrderByRedisIncrement(Long seckillId, String userId) {
	long orderId = idWorker.nextId();

	LOGGER.info("开始下单条件校验。商品ID={},订单ID {}", seckillId, orderId);
	TbSeckillGoods seckillGoods = (TbSeckillGoods) redisTemplate.opsForValue().get("seckill_goods_" + seckillId);
	HttpResult result = validGoods(seckillGoods);
	if (result.getCode() != SUCCESS) {
		return result;
	}
	// 扣减redis库存,increment是原子性操作
	Long response = redisTemplate.opsForValue().increment("seckill_goods_stock_" + seckillId, -1);

	if (response <= 0) {
		// 库存扣减成功
		return HttpResult.error("商品已售罄");
	}

	LOGGER.info("开始下单,订单号：{}", orderId);
	TbSeckillOrder seckillOrder = newSkillOrder(orderId, seckillGoods, userId);
	//保存订单
	seckillOrderMapper.insertSelective(seckillOrder);

	return HttpResult.ok(orderId + "");
}

	/**
	 * 7-1 使用zookeeper分布式锁方案控制库存，实现下单
	 * @author hubin
	 * @date 2020/9/5 20:48
	 */
	@Transactional
	@Override
	public HttpResult startSubmitOrderByZookeeperLock(Long seckillId, String userId) {
		long orderId = idWorker.nextId();

		//加锁初始值
		boolean isLock = false;
		try {
			isLock = ZkLockUtils.acquire(3, TimeUnit.SECONDS);

			LOGGER.info("开始下单条件校验。商品ID={},订单ID {}", seckillId, orderId);
			TbSeckillGoods seckillGoods = (TbSeckillGoods) redisTemplate.opsForValue().get("seckill_goods_" + seckillId);

			HttpResult result = validGoods(seckillGoods);
			if (result.getCode() != SUCCESS) {
				return result;
			}

			LOGGER.info("开始扣减库存 订单ID {}", seckillId);
			DeductionInventory(seckillId, orderId, seckillGoods);
			// 将seckillGoos减库存进行录入到 redis
			redisTemplate.opsForValue().set("seckill_goods_" + seckillId, seckillGoods);

			LOGGER.info("开始下单,订单号：{}", orderId);
			TbSeckillOrder seckillOrder = newSkillOrder(orderId, seckillGoods, userId);
			//保存订单
			seckillOrderMapper.insertSelective(seckillOrder);

		} catch (RuntimeException e) {
			e.printStackTrace();
		}finally {
			//必须要释放锁
			if(isLock){
				ZkLockUtils.release();
			}
		}
	   return HttpResult.ok(orderId+"");
	}

	/**
	 * 8-1 使用RocketMQ队列，实现下单
	 * @author hubin
	 * @date 2020/9/5 20:48
	 */
	@Override
	public HttpResult startSubmitOrderByRcoketMQ(Long seckillId, String userId) {
		LOGGER.info("开始下单条件校验。商品ID={}", seckillId);
		TbSeckillGoods seckillGoods = (TbSeckillGoods) redisTemplate.opsForValue().get("seckill_goods_" + seckillId);

		HttpResult result = validGoods(seckillGoods);
		if (result.getCode() != SUCCESS) {
			return result;
		}

		LOGGER.info("开始扣减库存 订单ID {}", seckillId);
		Long increment = redisTemplate.opsForValue().increment("seckill_goods_stock_" + seckillId, -1);
		//判断扣减库存是否成功
		if (increment > 0) {
			// 库存扣减成功
			boolean isMqSender = mqSender.transactionAsyncReduceStock("goods_stock_count", seckillId + "", userId);
			LOGGER.info("库存扣减后结果:{}", isMqSender);
			if (!isMqSender){
				// 没有发送出去，订单失败，库存进行回滚
				redisTemplate.opsForValue().increment("seckill_goods_stock_" + seckillId, 1);
				return HttpResult.error();
			}
		} else {
			// 扣减失败，库存进行回滚
			redisTemplate.opsForValue().increment("seckill_goods_stock_" + seckillId, 1);
			return HttpResult.error("商品已售罄");
		}
		return HttpResult.ok();
	}

	/**
	 * 下单-添加订单
	 * @author linhx
	 * @date 2021/1/31 15:10
	 */
	@Override
	public void placeOrder(long seckillId, String userId) {
		long orderId = idWorker.nextId();
		TbSeckillGoods seckillGoods = (TbSeckillGoods) redisTemplate.opsForValue().get("seckill_goods_" + seckillId);
		TbSeckillOrder seckillOrder = newSkillOrder(orderId, seckillGoods, userId);
		seckillOrderMapper.insertSelective(seckillOrder);
	}

	/**
	 * 支付完毕，更新订单的状态
	 * @author hubin
	 * @date 2020/6/10 22:20
	 */
	@Override
	@Transactional
	public void updateOrderStatus(String out_trade_no, String transaction_id) {
		//1.修改支付日志状态
		TbPayLog payLog = payLogMapper.selectByPrimaryKey(out_trade_no);
		//支付时间
		payLog.setPayTime(new Date());
		//交易状态
		payLog.setTradeState("1");
		//流水号
		payLog.setTransactionId(transaction_id);
		payLogMapper.updateByPrimaryKey(payLog);

		//2.修改订单状态
		String orderList = payLog.getOrderList();
		String[] ids = orderList.split(",");//订单号
		for(String id:ids){

			TbSeckillOrder order = seckillOrderMapper.selectByPrimaryKey( Long.valueOf(id) );
			order.setStatus("2");//支付状态
			order.setPayTime(new Date());//支付时间
			seckillOrderMapper.updateByPrimaryKey(order);
		}
	}


	@Override
	public TbPayLog searchPayLogFromRedis(String userId) {
		return (TbPayLog) redisTemplate.boundHashOps("payLog").get(userId);
	}

	/**
	 * 根据token查询用户信息
	 * @author hubin
	 * @date 2020/6/10 10:47
	 */
	@Override
	public FrontUser getUserInfoFromRedis(String token) {
		return (FrontUser) redisTemplate.opsForValue().get(token);
	}

	@Override
	public HttpResult getOrderMoney(Long orderId) {
		TbSeckillOrder order = seckillOrderMapper.selectByPrimaryKey(orderId);
		HttpResult httpResult = new HttpResult();
		httpResult.setData(order.getMoney());
		return httpResult;
	}

	@Override
	public TbSeckillOrder findOrderById(Long orderId) {
		return seckillOrderMapper.selectByPrimaryKey(orderId);
	}
}
